package jgespres;

import java.sql.*;
import java.util.*;
import java.awt.Point;
import java.util.logging.*;
import javax.swing.table.AbstractTableModel;

/**
 * Gestion d'une classe d'élève
 * 
 * @author guerfyld
 * @version 2.0
 */
public class SchoolClass extends AbstractTableModel {
    // Connection à la Base de données
    private Connection conn;
    // Requêtes préparées
    private PreparedStatement selectStudents;
    private PreparedStatement insertStudent;
    private PreparedStatement updateStudent;
    private PreparedStatement deleteStudent;
    private PreparedStatement maxStudentIds;
    private PreparedStatement updStudentOrd;
    //private PreparedStatement orderedStudent;
    private Statement stmt;
    
    // Liste des élèves de la classe
    private Map  listById;
    private List orderList;
    
    // Logger
    private Logger logger = Logger.getLogger("jgespres");;

    /**
     * Constructeur
     * 
     * @param connection   Connection à une base JDBC
     */
    public SchoolClass(Connection connection) {
        // Initialisations
        listById  = new HashMap(100);
        orderList = new ArrayList(100);            
        setConnection(connection);
    }

    /**
     * Affectation connection (Changment de base)
     * 
     * @param connection   Connection à une base JDBC
     */
    public void setConnection(Connection connection) {
        // Affectation connection
        conn = connection;
        try {
            // Préparation requêtes
            selectStudents = conn.prepareStatement("SELECT id, libS, libL, groupe, extra, nord FROM Students ORDER BY nord", 
                                ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
            insertStudent  = conn.prepareStatement("INSERT INTO Students (id, libS, libL, groupe, extra, nord) VALUES(?, ?, ?, ?, ?, ?)");
            updateStudent  = conn.prepareStatement("UPDATE Students SET libS = ?, libL = ?, groupe = ?, extra = ? WHERE id = ?");
            deleteStudent  = conn.prepareStatement("DELETE FROM Students WHERE id = ?");
            updStudentOrd  = conn.prepareStatement("UPDATE Students SET nord = ? WHERE id = ?");
            maxStudentIds  = conn.prepareStatement("SELECT count(*), max(id), max(nord) FROM Students");
            //orderedStudent = conn.prepareStatement("SELECT max(t.date), p.idStd FROM Planning2 p join themes t on p.idThm = t.id GROUP BY p.idStd ORDER BY max(t.date)");
            stmt = conn.createStatement();
            
            // Chargement initial de la liste des élèves
            load(true, false);
            fireTableStructureChanged();
        }
        catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * Chargement initial de la liste des élèves de la classe
     * 
     * @param init          Vrai si initialisation, Faux si rechargement.
     * @param recalcNOrd    Vrai si recalcul des N° d'ordre.
     * @return      Opération effectuée avec succès (Vrai/Faux)
     */
    private boolean load(boolean init, boolean recalcNOrd) {
        Student Std;
        
        logger.log(Level.INFO, "Chargement SchoolClass...");
        // Initialisation listes des Elèves
        if (init) listById.clear();
        orderList.clear();
        int orderNum = 10;

        try {
            // Chargement liste des Elèves
            ResultSet rs = selectStudents.executeQuery();
            while (rs.next()) {
                if (init) {
                    // Mise à jour infos Elève
                    Std = new Student();
                    Std.setId(rs.getInt(1));
                    Std.setIdent(rs.getString(2));
                    Std.setName(rs.getString(3));
                    Std.setGroup(rs.getString(4));
                    Std.setExtra(rs.getString(5));
                    listById.put(new Integer(Std.getId()), Std);
                }
                else {
                    // Récupèration infos Elève
                    Std = (Student) listById.get(new Integer(rs.getInt(1)));
                    if (Std == null) {
                        logger.log(Level.SEVERE, "ERREUR Eleve id="+rs.getInt(1)+" non trouvé dans SchoolClass !");
                        continue;
                    }
                }

                // Mise à jour positionnement Elève + Ajout liste
                Std.setOrderPos(orderList.size());
                logger.log(Level.FINEST, "["+orderList.size()+"]="+Std);
                orderList.add(Std);
                
                // Recalcul les n° d'ordre
                if (recalcNOrd) {
                    rs.updateInt(6, orderNum);
                    rs.updateRow();
                    orderNum += 10;
                }
            }
            
            logger.log(Level.CONFIG, "- orderList="+orderList.size());
            logger.log(Level.CONFIG, "- listById="+listById.size());
            logger.log(Level.INFO, "Chargement SchoolClass ok.");
            return true;
        }
        catch(Exception e) {
            logger.log(Level.SEVERE, "ERREUR Chargement SchoolClass :");
            e.printStackTrace();
        }

        return false;
    }

    /**
     * Rechargement de la liste des élèves de la classe
     * 
     * @return      Opération effectuée avec succès (Vrai/Faux)
     */
    public boolean reload() {
        return load(false, false);
    }
    
    /**
     * Renvoi le nombres d'élèves de la classe
     * 
     * @return      Nombres d'élèves dans classe
     */
    public int getNbStudents() {
        return listById.size();
    }

    /**
     * Renvoi l'élève au n-éme rang de la classe
     * 
     * @param orderPos   Rang de l'élève (0..n)
     * @return           Elève
     */
    public Student getStudent(int orderPos) {
        return (Student) orderList.get(orderPos);
    }
    
    /**
     * Renvoi un élève de la classe
     * 
     * @param  id       Id technique de l'élève
     * @return           Elève
     */
    public Student getStudentById(int id) {
        return (Student) listById.get(new Integer(id));
    }
    
    /**
     * Renvoi rang d'un élève de la classe
     * 
     * @param  id       Id technique de l'élève
     * @return          Rang de l'élève (0..n)
     */
    public int getStudentPos(int id) {
        Student Std = (Student) listById.get(new Integer(id));
        if (Std != null)
            return Std.getOrderPos();
        
        return -1;
    }

    /**
     * Renvoi liste des élèves appartenant à un groupe
     * 
     * @param group Nom du groupe
     * @return Liste de élèves du groupe
     */
    public Student[] getStudents(String group) {
        ArrayList result = new ArrayList(100);
        
        for(int i=0; i<getNbStudents(); i++) {
            Student Std = getStudent(i);
            if (Std.getGroup().equals(group)) result.add(Std);
        }
        
        return (Student[]) result.toArray(new Student[result.size()]);
    }

    /**
     * Renvoi liste des élèves appartenant à un groupe
     * 
     * @param group Nom du groupe
     * @return Liste de élèves du groupe
     */
    public Student[] getStudentsOrderedByThm(String group) {
        ArrayList result = new ArrayList(100);
        
        try {
            // Chargement liste des Elèves
            //ResultSet rs = orderedStudent.executeQuery();
            ResultSet rs = stmt.executeQuery("SELECT max(t.date), p.idStd FROM Planning2 p join themes t on p.idThm = t.id GROUP BY p.idStd ORDER BY max(t.date)");
            while (rs.next()) {
                int i = rs.getInt(2);
                Student Std = getStudentById(i);
                if ((Std != null) && (Std.getGroup().equals(group))) result.add(Std);
            }
        }
        catch(Exception e) {
            logger.log(Level.SEVERE, "ERREUR Chargement liste d'élèves triés :");
            e.printStackTrace();
        }
        
        return (Student[]) result.toArray(new Student[result.size()]);
    }
    
    /**
     * Ajoute Elève au dernier rang de la classe
     * 
     * @param Std       Elève
     * @return          Opération effectuée avec succès (Vrai/Faux)
     */
    public boolean addStudent(Student Std) {
        ResultSet rs;
        int orderNum;
        int id;
        
        try {
            // Calcul nouvel id interne
            rs = maxStudentIds.executeQuery();
            if (rs.next()) {
                if (rs.getInt(1) != 0) {
                    id = rs.getInt(2) + 1;
                    orderNum = rs.getInt(3) + 10;
                }
                else {
                    id = 1;
                    orderNum = 10;
                }
                // Mise à jour Base de données
                logger.log(Level.INFO, "Ajout Eleve id=" + id);
                insertStudent.setInt(1, id);
                insertStudent.setString(2, Std.getIdent());
                insertStudent.setString(3, Std.getName());
                insertStudent.setString(4, Std.getGroup());
                insertStudent.setString(5, Std.getExtra());
                insertStudent.setInt(6, orderNum);
                insertStudent.executeUpdate();
                
                // Mise à jour Liste des éleves
                Std.setId(id);
                listById.put(new Integer(Std.getId()), Std);
                
                return reload();
            }
        }
        catch(Exception e) {
            e.printStackTrace();
        }    
        
        return false;
    }
    
    /**
     * Enlève un élève à la classe
     * 
     * @param orderPos   Rang de l'élève (0..n)
     * @return           Opération effectuée avec succès (Vrai/Faux)
     */
    public boolean delStudent(int orderPos) {
        Student Std = getStudent(orderPos);
        int id = Std.getId();
        logger.log(Level.FINE, "Suppression élève ("+id+")");
        try {
            // Mise à jour Base de données
            deleteStudent.setInt(1, id);
            deleteStudent.executeUpdate();

            // Mise à jour Liste des éleves
            listById.remove(new Integer(Std.getId()));
            return load(false, true);
        }
        catch(Exception e) {
            logger.log(Level.SEVERE, "Erreur suppression élève ("+id+") :");
            e.printStackTrace();
        }
        
        return false;
    }
    
    /**
     * Déplace élève du n-éme au m-éme rang de la classe
     * 
     * @param oldPos    Rang initial de l'élève (0..n)
     * @param newPos    Nouveau rang de l'élève (0..n)
     * @return          Opération effectuée avec succès (Vrai/Faux)
     */
    public boolean moveStudent(int oldPos, int newPos) {
        int orderNum;
        
        if ((oldPos >= 0) && (newPos >= 0)) {
            // Determine élève à déplacer
            int id = getStudent(oldPos).getId();
            
            // Determine nouveau "nord" à affecter
            if (newPos < getNbStudents())
                orderNum = (newPos * 10) + 5;
            else
                orderNum = (newPos * 10) + 10;

            try {
                // Affecte le nouveau N° d'ordre
                logger.log(Level.INFO, "Mise à jour Eleve id=" + id);
                updStudentOrd.setInt(1, orderNum);
                updStudentOrd.setInt(2, id);
                updStudentOrd.executeUpdate();
           
                // Réordonne liste des éleves
                return load(false, true);
            }
            catch(Exception e) {
                e.printStackTrace();
            }
        }
        
        return false;   
    }
    
    /**
     * Mise à jour d'un Elève
     * 
     * @param Std       Elève à mettre à jour
     * @return          Opération effectuée avec succès (Vrai/Faux)
     */
    public boolean updStudent(Student Std) {
        int id = Std.getId();
        logger.log(Level.FINE, "Sauvegarde élève ("+id+")");
        try {
            updateStudent.setString(1, Std.getIdent()); 
            updateStudent.setString(2, Std.getName());
            updateStudent.setString(3, Std.getGroup());
            updateStudent.setString(4, Std.getExtra());
            updateStudent.setInt(5, id);
            updateStudent.executeUpdate();
            
            return true;
        }
        catch(Exception e) {
            logger.log(Level.SEVERE, "Erreur sauvegarde élève ("+id+") :", e);
            e.printStackTrace();
        }

        return false;
    }
        
    //    *********************************
    //    AbstractTableModel Implementation
    //    *********************************
    
    /**
     * Renvoi le nom d'une colonne de la JTable
     * 
     * @param col    Numéro de la colonne (0..n)
     * @return       Nom de la colonne
     */
    public String getColumnName(int col) {
        try {
            return jgespres.getResourceString("Student.Col" + Integer.toString(col));
        }
        catch(Exception e) {}

        return "";
    }

     /**
      * Renvoi le nombre de colonne de la JTable
      * 
      * @return       Nombre de colonnes
      */
     public int getColumnCount() {
          return 4;
     }

     /**
      * Renvoi le nombre de lignes de la JTable
      * 
      * @return       Nombre de lignes
      */
     public int getRowCount() {
          return getNbStudents();
     }

     /**
      * Renvoi valeur d'une cellule de la JTable
      * 
      * @param row      Numéro de ligne de la JTable
      * @param col      Numéro de colonne de la JTable
      * @return         Valeur de la cellule
      */
     public Object getValueAt(int row, int col) { 
         switch(col) {
            case 0:
                return getStudent(row).getIdent();
            case 1:
                return getStudent(row).getName();
            case 2:
                return getStudent(row).getGroup();
            case 3:
                return getStudent(row).getExtra();
         }

         return "";
     }

     /**
      * Renvoi si cellule de la JTable est éditable
      * 
      * @param row      Numéro de ligne de la JTable
      * @param col      Numéro de colonne de la JTable
      * @return         Cellule Editable (Vrai/Faux)
      */
     public boolean isCellEditable(int row, int col) {
          return true;
     }

     /**
      * Mise à jour valeur d'une cellule de la JTable
      * 
      * @param row      Numéro de ligne de la JTable
      * @param col      Numéro de colonne de la JTable
      */
     public void setValueAt(Object value, int row, int col) {
         Student Std = getStudent(row);
         
         switch(col) {
            case 0:
                Std.setIdent((String) value);
                break;
            case 1:
                Std.setName((String) value);
                break;
            case 2:
                Std.setGroup((String) value);
                break;
            case 3:
                Std.setExtra((String) value);
                break;
        }
        updStudent(Std);
     }
}
